package com.coder4j.starter.web;

import com.coder4j.starter.mybatis.*;
import com.coder4j.starter.tools.util.DateUtils;
import com.coder4j.starter.tools.util.ReflectUtils;
import com.coder4j.starter.web.jwt.AuthVO;
import com.coder4j.starter.web.util.AuthUtils;
import com.coder4j.starter.web.util.SpringContextUtils;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationContext;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @param <T>
 * @author Keller
 * @date 2022-01-06 20:47:18
 * Server基类,其他Service直接继承该类即可
 */
@Slf4j
public abstract class BaseServiceImpl<M extends BaseMapper<T>, T,V>  implements IBaseService<T,V>{

    private M baseMapper = null;
    private T baseEntity = null;

    /**
     * 获取Mapper
     *
     * @return
     */
    private synchronized M getMapper() {
        ApplicationContext context = SpringContextUtils.getApplicationContext();
        if (baseMapper == null) {
            Type genType = this.getClass().getGenericSuperclass();
            if (genType instanceof ParameterizedType) {
                Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
                baseMapper = context.getBean((Class<M>) params[0]);
            }
        }
        return baseMapper;
    }

    /**
     * 获取对象实例
     *
     * @return
     */
    private synchronized T getEntity() {
        if (baseEntity == null) {
            Type genType = this.getClass().getGenericSuperclass();
            if (genType instanceof ParameterizedType) {
                Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
                Class<T> clazz = (Class<T>) params[1];
                try {
                    baseEntity = clazz.newInstance();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return baseEntity;
    }

    @Override
    public V baseSaveVO(V vo){
        T entity = this.vo2Entity(vo);
        this.baseSave(entity);
        return this.entity2Vo(entity);
    }

    /**
     * 保存数据
     * @param entity 数据对象
     * @return 保存的数据
     */
    @Override
    public T baseSave(T entity) {
        this.initBaseData(entity);

        //没有ID，添加数据
        if (!ReflectUtils.hasValue(entity, BaseEntityEnums.id.name())) {
            try {
                long key = getMapper().baseInsertAndReturnKey(entity);
                try {
                    ReflectUtils.setValue(entity, BaseEntityEnums.id.name(), key);
                } catch (Exception e) {
                    log.error("setId error", e);
                }
            } catch (Exception e) {
                this.getMapper().baseInsert(entity);
            }
            return entity;
        }
        T result = this.getMapper().baseSelectById(entity);

        //根据ID查询不到，添加数据
        if (result == null) {
            throw new RuntimeException("更新失败，数据不存在");
        }

        //能查询到，修改数据
        this.getMapper().baseUpdateById(entity);
        return entity;
    }

    private void initBaseData(T entity){
        Class clazz = entity.getClass();
        //如果继承了基类：新增时，写入 created,createName,createId；编辑时，写入 updated,updateName,updateId
        if (BaseEntity.class.isAssignableFrom(clazz)) {

            //从 AuthUtils 中取出当前操作人的 id、name
            AuthVO authVO = AuthUtils.getAuthVO();
            String userName = authVO == null ? null : authVO.getUserName();
            Long userId = authVO == null ? null : authVO.getUserId();
            if (ReflectUtils.hasValue(entity, BaseEntityEnums.id.name())) {
                ReflectUtils.setValue(entity, BaseEntityEnums.updateTime.name(), new Date());
                if (userId != null) {
                    ReflectUtils.setValue(entity, BaseEntityEnums.updateId.name(), userId);
                }
                if (userName != null) {
                    ReflectUtils.setValue(entity, BaseEntityEnums.updateName.name(), userName);
                }
            } else {
                ReflectUtils.setValue(entity, BaseEntityEnums.createTime.name(), new Date());
                if (userId != null) {
                    ReflectUtils.setValue(entity, BaseEntityEnums.createId.name(), userId);
                }
                if (userName != null) {
                    ReflectUtils.setValue(entity, BaseEntityEnums.createName.name(), userName);
                }
            }
        }
        //如果继承了软删除：新增时，isDeleted 默认为 0
        if (BaseSoftDeleteEntity.class.isAssignableFrom(clazz)
                && !ReflectUtils.hasValue(entity, BaseEntityEnums.id.name())) {
            ReflectUtils.setValue(entity, BaseEntityEnums.isDeleted.name(), 0);
        }
    }

    @Override
    public int baseSaveVoList(List<V> voList){
        if(voList == null | voList.isEmpty()){
            return 0;
        }
        List<T> list = new ArrayList<>();
        for (V vo : voList) {
            list.add(this.vo2Entity(vo));
        }
        return this.baseSaveList(list);
    }
    /**
     * 批量保存
     * @param list 数据列表
     * @return 影响的数据条数
     */
    @Override
    public int baseSaveList(List<T> list) {
        if (list == null || list.isEmpty()) {
            return 0;
        }
        for (T entry : list) {
            this.initBaseData(entry);
        }
        return this.getMapper().baseInsertList(list);
    }

    @Override
    public V baseGetVOById(@NonNull Object id){
        T entity = this.baseGetById(id);
        return entity == null ? null : this.entity2Vo(entity);
    }
    /**
     * 根据ID查询
     * @param id ID
     * @return 查询结果
     */
    @Override
    public T baseGetById(@NonNull Object id) {
        T entity = this.getEntity();
        ReflectUtils.setValue(entity, BaseEntityEnums.id.name(), id);
        return this.getMapper().baseSelectById(entity);
    }

    /**
     * 查询多个
     * @param ids
     * @return
     */
    @Override
    public List<T> baseGetListByIds(List ids) {
        if (ids == null || ids.isEmpty()) {
            return null;
        }
        T entity = this.getEntity();
        return this.getMapper().baseSelectByIdList(entity, ids);
    }

    /**
     * 根据ID删除
     *
     * @param id ID
     * @return 删除的数据
     */
    @Override
    public int baseDeleteById(Object id) {
        T entity = getEntity();
        Class<?> clazz = entity.getClass();
        ReflectUtils.setValue(entity, BaseEntityEnums.id.name(), id);
        //如果继承了软删除，使用软删除
        if (BaseSoftDeleteEntity.class.isAssignableFrom(clazz)) {
            ReflectUtils.setValue(entity, BaseEntityEnums.isDeleted.name(), 1);
            return this.baseUpdateById(entity);
        }
        return this.getMapper().baseDeleteById(entity);
    }

    @Override
    public int baseDeleteByIds(List ids) {
        T entity = getEntity();
        return this.getMapper().baseDeleteByIds(entity,ids);
    }

    @Override
    public int baseUpdateById(T entity) {
        return getMapper().baseUpdateById(entity);
    }

    /**
     * 根据Id批量修改，适用于批量修改一批数据的指定字段
     *
     * @param entity 要修改的数据，只修改其中有值的字段
     * @param ids
     * @return
     */
    @Override
    public int baseUpdateByIds(T entity, List ids) {
        return getMapper().baseUpdateByIds(entity, true, ids);
    }

    /**
     * 实体对象转VO，子类使用时需要重写该方法
     * @param entity
     * @return
     */
    public abstract  V entity2Vo(T entity) ;

    /**
     * VO转实体对象
     * @param vo
     * @return
     */
    public abstract T vo2Entity(V vo);

    /**
     * 查询一个
     * @param query
     * @return
     */
    @Override
    public T baseQueryOne(BaseQuery<T> query) {
        List<T> list = getMapper().baseSelectList(query);
        return list == null || list.isEmpty() ? null : list.get(0);
    }

    /**
     * 查询一个VO
     * @param query
     * @return
     */
    @Override
    public V baseQueryOneVO(BaseQuery<T> query) {
        T entity = baseQueryOne(query);
        return entity == null ? null : entity2Vo(entity);
    }
    @Override
    public BasePage<T> baseQueryPage(BaseQuery<T> query) {
        return getMapper().baseSelectPage(query);
    }

    @Override
    public List<T> baseQueryList(BaseQuery<T> query) {
        return getMapper().baseSelectList(query);
    }

    /**
     * 分页查询VO
     *
     * @param query
     * @return
     */
    @Override
    public BasePage<V> baseQueryVOPage(BaseQuery<T> query) {
        log.info("====baseQueryVOPage,{}",AuthUtils.getDataScope());

        //实体对象列表
        BasePage<T> entityPage = getMapper().baseSelectPage(query);

        //实体类列表
        BasePage<V> voPage = new BasePage<>();
        BeanUtils.copyProperties(entityPage, voPage);

        List<T> entityList = entityPage.getDataList();
        if (entityList == null || entityList.isEmpty()) {
            return voPage;
        }

        //转换
        List<V> voList = new ArrayList<>();
        for (T entity : entityList) {
            voList.add(this.entity2Vo(entity));
        }
        voPage.setDataList(voList);
        return voPage;
    }

    /**
     * 查询VO列表
     * @param query
     * @return
     */
    @Override
    public List<V> baseQueryVOList(BaseQuery<T> query) {
        List<V> voList = new ArrayList<>();
        List<T> entityList = getMapper().baseSelectList(query);
        if (entityList == null || entityList.isEmpty()) {
            return voList;
        }
        //转换
        for (T entity : entityList) {
            voList.add(this.entity2Vo(entity));
        }
        return voList;
    }

    public <TREE extends BaseTreeVO> void  baseInitChildren(TREE root, List<TREE> list){
        List<TREE> subList = list.stream()
                .filter(item -> item.getParentId().equals(root.getId()))
                .collect(Collectors.toList());
        if(subList.isEmpty()){
            return;
        }
        root.setChildren(subList);

        List<TREE> otherVOs = list.stream().filter(item -> !item.getParentId().equals(root.getId()))
                .collect(Collectors.toList());
        for (TREE treeVO : subList) {
            this.baseInitChildren(treeVO,otherVOs);
        }
    }
}
